package robotspeech;
import edu.cmu.sphinx.frontend.util.Microphone;
import edu.cmu.sphinx.recognizer.Recognizer;
import edu.cmu.sphinx.result.Result;
import edu.cmu.sphinx.util.props.ConfigurationManager;

import java.io.File;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.Authenticator.RequestorType;
import java.util.*;
import java.util.concurrent.*;
import java.io.*;


public class SpeechProcessor {

	public static enum SpeechResponseType
	{
		EXCEPTION,MISUNDERSTOOD,STANDARD_RESPONSE,EXITING;
	}
	
	public class SpeechResponse
	{
		SpeechResponseType responseType;
		String responseString;
		public SpeechResponse(String text, SpeechResponseType type)
		{
			responseType=type;
			responseString=text;
		}
		
		public SpeechResponseType getResponseType()
		{
			return responseType;
		}
		
		public String getResponseString()
		{
			return responseString;
		}
	}
	
	
	public enum LoopControl
	{
		REPEAT,QUIT;
	}
	
	BlockingQueue<LoopControl> loopQueue;
	BlockingQueue<Object> outputQueue;
	String speechConfigFileFullname;
	Thread processThread;
	
	public SpeechProcessor(BlockingQueue<Object> responseQueue, String configFileFullName)
	{
		loopQueue = new LinkedBlockingQueue<LoopControl>();
		outputQueue = responseQueue;
		speechConfigFileFullname=configFileFullName;
		Runnable interpretThread = new Runnable()
		{
			public void run()
			{
				try
				{
					interpret();
				}
				catch (Exception e)
				{
					try
					{
						outputQueue.put(new SpeechResponse(e.toString(),SpeechResponseType.EXCEPTION));
					}
					catch (Exception r)
					{
						StringWriter  sWriter = new StringWriter();
						PrintWriter pWriter = new PrintWriter(sWriter,true);
						r.printStackTrace(pWriter);
						pWriter.close();
						throw new RuntimeException(sWriter.toString());
					}
				}
				
			}
		};
		processThread = new Thread(interpretThread);
	}
	
	public SpeechProcessor(String configFileFullName)
	{
		loopQueue = new LinkedBlockingQueue<LoopControl>();
		outputQueue = new LinkedBlockingQueue<Object>();
		speechConfigFileFullname=configFileFullName;
		Runnable interpretThread = new Runnable()
		{
			public void run()
			{
				try
				{
					interpret();
				}
				catch (Exception e)
				{
					try
					{
						outputQueue.put(new SpeechResponse(e.toString(),SpeechResponseType.EXCEPTION));
					}
					catch (Exception r)
					{
						StringWriter  sWriter = new StringWriter();
						PrintWriter pWriter = new PrintWriter(sWriter,true);
						r.printStackTrace(pWriter);
						pWriter.close();
						throw new RuntimeException(sWriter.toString());
					}
				}
				
			}
		};
		processThread = new Thread(interpretThread);
	}
	
	
	public BlockingQueue<Object> GetSpeechQueue()
	{
		return outputQueue;
	}
	
	
	public boolean StartProcessing() 
	{	
		if (!processThread.isAlive())
		{
			processThread.start();
			return true;
		}
		return false;
	}
	
	
	
	public void StopProcessing()
	{
		try
		{
			loopQueue.put(LoopControl.QUIT);
		}
		catch (Exception e)
		{
			throw new RuntimeException(e.toString());
		}
	}
	
	
	public static void interpret(int wordCount,String configFileFullName,Queue<String> response,String errorResponse) throws Exception
	{
		URL url;
		url = new File(configFileFullName).toURI().toURL();
        //url = TestHarness.class.getResource();
        

        ConfigurationManager cm = new ConfigurationManager(url);

        // allocate the recognizer
        Recognizer recognizer = (Recognizer) cm.lookup("recognizer");
        recognizer.allocate();

        // start the microphone
        Microphone microphone = (Microphone) cm.lookup("microphone");
        microphone.startRecording();

        // loop the recognition until the programm exits.
        String resultText;
        for (int i=0;i<wordCount;i++) 
        {
        	System.out.println("Start speaking.\n");

            Result result = recognizer.recognize();

            if (result != null) {
                resultText = result.getBestResultNoFiller();
                response.add(resultText);
            } else {
            	response.add(errorResponse);;
            }
        }
        microphone.stopRecording();
        recognizer.deallocate();
	}
	
	public static void interpret(int wordCount,String configFileFullName) throws Exception
	{
		URL url;
		url = new File(configFileFullName).toURI().toURL();
        //url = TestHarness.class.getResource();
        

        ConfigurationManager cm = new ConfigurationManager(url);

        // allocate the recognizer
        Recognizer recognizer = (Recognizer) cm.lookup("recognizer");
        recognizer.allocate();

        // start the microphone
        Microphone microphone = (Microphone) cm.lookup("microphone");
        microphone.startRecording();

        // loop the recognition until the programm exits.
        String resultText;
        for (int i=0;i<wordCount;i++) 
        {
        	System.out.println("Start speaking.\n");

            Result result = recognizer.recognize();

            if (result != null) {
                resultText = result.getBestResultNoFiller();
                System.out.println(resultText);
            } else {
            	System.out.println("fail to understand");
            }
        }
        microphone.stopRecording();
        recognizer.deallocate();
	}
	
	private void interpret() throws Exception
	{
		URL url;
		url = new File(speechConfigFileFullname).toURI().toURL();
        //url = TestHarness.class.getResource();
        

        ConfigurationManager cm = new ConfigurationManager(url);

        // allocate the recognizer
        Recognizer recognizer = (Recognizer) cm.lookup("recognizer");
        recognizer.allocate();

        // start the microphone
        Microphone microphone = (Microphone) cm.lookup("microphone");
        microphone.startRecording();

         // loop the recognition until the programm exits.
        
        LoopControl control = LoopControl.REPEAT;
        
        loopQueue.put(control);
        System.out.println("Listening for commands.\n");
        SpeechResponse response;
        String resultText;
        String pronun;
        while ((control=loopQueue.take())!=LoopControl.QUIT)
        {
            
        	
            Result result = recognizer.recognize();

            if (result != null) {
            	//resultText = result.getBestFinalResultNoFiller();
            	resultText = result.getBestResultNoFiller();
            	pronun=result.getBestPronunciationResult();
            	System.out.println(pronun + "\n");
                response = new SpeechResponse(resultText,SpeechResponseType.STANDARD_RESPONSE);
                
            } else {
            	
            	response = new SpeechResponse("",SpeechResponseType.MISUNDERSTOOD);
            	
            }
            outputQueue.put(response);
            loopQueue.put(LoopControl.REPEAT);
        }
        //System.out.println("Done");
        outputQueue.put(new SpeechResponse("exiting",SpeechResponseType.EXITING));
        microphone.stopRecording();
        //System.out.println("deallocating recognizer");
        recognizer.deallocate();
        //System.out.println("deallocation done");
	}
}
